// Joypad events handler

import emitter from "./emitter";
import joypad from "./joypad";
import loop from "./loop";
import { EVENTS, STICKS, DIRECTIONS, BUTTON_MAPPING } from "./constants";
import { findButtonMapping } from "./helpers";

const initEventListeners = () => {
  window.addEventListener(EVENTS.CONNECT.NATIVE, (e) => {
    emitter.publish(EVENTS.CONNECT.ALIAS, e);

    // Start loop on gamepad connection if not already started
    if (!joypad.loopStarted) {
      joypad.loopStarted = true;
      return loop.start();
    }
  });
  window.addEventListener(EVENTS.DISCONNECT.NATIVE, (e) => {
    emitter.publish(EVENTS.DISCONNECT.ALIAS, e);

    // Remove instance and reset events on gamepad disconnection
    joypad.remove(e.gamepad.index);
    joypad.buttonEvents.joypad[e.gamepad.index] = null;

    // Stop loop if all gamepads have been disconnected
    if (!Object.keys(joypad.instances).length) {
      joypad.loopStarted = false;
      return loop.stop(loop.id);
    }
  });
  window.addEventListener(EVENTS.BUTTON_PRESS.ALIAS, (e) => {
    return emitter.publish(EVENTS.BUTTON_PRESS.ALIAS, e);
  });
  window.addEventListener(EVENTS.BUTTON_RELEASE.ALIAS, (e) => {
    return emitter.publish(EVENTS.BUTTON_RELEASE.ALIAS, e);
  });
  window.addEventListener(EVENTS.AXIS_MOVEMENT.ALIAS, (e) => {
    return emitter.publish(EVENTS.AXIS_MOVEMENT.ALIAS, e);
  });
};
const listenToButtonEvents = (gamepad) => {
  gamepad.buttons.forEach((button, index) => {
    const { customButtonMapping } = joypad.settings;
    const buttonMapping = customButtonMapping
      ? customButtonMapping
      : BUTTON_MAPPING;
    const keys = findButtonMapping(index, buttonMapping);
    const { buttonEvents } = joypad;

    if (keys && keys.length) {
      keys.forEach((key) => {
        // If button is pressed then set press status of button
        if (button.pressed) {
          if (!buttonEvents.joypad[gamepad.index][key]) {
            buttonEvents.joypad[gamepad.index][key] = {
              pressed: true,
              hold: false,
              released: false,
            };
          }

          // Set button event data
          buttonEvents.joypad[gamepad.index][key].button = button;
          buttonEvents.joypad[gamepad.index][key].index = index;
          buttonEvents.joypad[gamepad.index][key].gamepad = gamepad;
        }

        // If button is not pressed then set release status of button
        else if (!button.pressed && buttonEvents.joypad[gamepad.index][key]) {
          buttonEvents.joypad[gamepad.index][key].released = true;
          buttonEvents.joypad[gamepad.index][key].hold = false;
        }
      });
    }
  });
};

let lastAxes = [0, 0, 0, 0];

// 判断遥感是否移动，比较当前axes与lastAxes的值
const isStickMoved = (axes) => {
  // 如果lastAxes不存在，视为移动
  if (!lastAxes) return true;

  // 比较每个轴的值，超过阈值视为移动
  const threshold = 0.05;
  for (let i = 0; i < axes.length; i++) {
    if (Math.abs(axes[i] - lastAxes[i]) > threshold) {
      return true;
    }
  }
  return false;
};

const listenToAxisMovements = (gamepad) => {
  const axisMovementEvent = (eventData) =>
    new CustomEvent(EVENTS.AXIS_MOVEMENT.ALIAS, { detail: eventData });
  // 处理axes值：保留4位小数，绝对值小于0.0001的值设为0
  const rawAxes = gamepad.axes;
  const axes = rawAxes.map((value) => {
    const rounded = Number(value.toFixed(4));
    return Math.abs(rounded) < 0.0001 ? 0 : rounded;
  });
  const totalAxisIndexes = axes.length;
  const totalSticks = totalAxisIndexes / 2;

  if (isStickMoved(axes)) {
    let stickMoved = "stick_moved";

    lastAxes = axes;
    const eventData = { gamepad, totalSticks, stickMoved, axes };
    return window.dispatchEvent(axisMovementEvent(eventData));
  }
};
const dispatchCustomEvent = (eventName, buttonEvents, buttonName) => {
  const joypadEvent = (eventData) =>
    new CustomEvent(eventName, { detail: eventData });
  const { index, gamepad } = buttonEvents[buttonName];
  const eventData = {
    buttonName,
    button: buttonEvents[buttonName].button,
    index,
    gamepad,
  };

  window.dispatchEvent(joypadEvent(eventData));
};
const handleButtonEvent = (buttonName, buttonEvents) => {
  // Fire button press event
  if (buttonEvents[buttonName].pressed) {
    dispatchCustomEvent(EVENTS.BUTTON_PRESS.ALIAS, buttonEvents, buttonName);

    // Reset button usage flags
    buttonEvents[buttonName].pressed = false;
    buttonEvents[buttonName].hold = true;
    // Set last button press to fire button release event
    buttonEvents[buttonName].last_event = EVENTS.BUTTON_PRESS.ALIAS;
  }

  // Button being held
  else if (buttonEvents[buttonName].hold) {
  }

  // Button being released
  else if (
    buttonEvents[buttonName].released &&
    buttonEvents[buttonName].last_event === EVENTS.BUTTON_PRESS.ALIAS
  ) {
    dispatchCustomEvent(EVENTS.BUTTON_RELEASE.ALIAS, buttonEvents, buttonName);

    delete buttonEvents[buttonName];
  }
};

initEventListeners();
export { listenToButtonEvents, listenToAxisMovements, handleButtonEvent };
